package com.wk.config;


import com.wk.bean.ResponseMsg;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.UUID;

/**
 * token验证处理
 *
 * @author ruoyi
 */
@Component
public class TokenService
{

    // 令牌自定义标识
    @Value("${token.header}")
    private static String header;

    // 令牌秘钥
    @Value("${token.secret}")
    private static String secret;

    // 令牌有效期（默认30分钟）
    @Value("${token.expireTime}")
    private static int expireTime;



    //使用UUID生成唯一标识jwt的id号
    private static UUID uuid = UUID.randomUUID();
    //秘钥生成,当然可以自定义自己的秘钥生成算法
    private static Key getKeyInstance() {
//        return MacProvider.generateKey();
        //We will sign our JavaWebToken with our ApiKey secret
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("serectKey");
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        return signingKey;
    }

    public static String createJWT(int UserId){

        //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        //生成过期时间 单位每分钟
        long expMillis = nowMillis + 30 *1000*60;
        Date exp = new Date(expMillis);
        //获取秘钥
        Key key = getKeyInstance();
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(String.valueOf(uuid))
                //iat: jwt的签发时间
                .setIssuedAt(now)
                //设置过期时间
                .setExpiration(exp)
                //加入用户信息 如： id号 还可以添加其他信息 key-value
                .claim("UserId", UserId)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, key);
        //压缩这三部分形成真正的jwt
        return builder.compact();
    }

    //通过serectKey解析jwt
    public static Claims parseJWT(String jwt)  {
        //签名秘钥，和生成的签名的秘钥一模一样
        Key key = getKeyInstance();
        Claims claims = Jwts.parser()  //得到DefaultJwtParser
                .setSigningKey(key)         //设置签名的秘钥
                .parseClaimsJws(jwt).getBody();//设置需要解析的jwt
        return claims;
    }

    //解析jwt，或者claim实例，通过key来取出相应的值
    public static ResponseMsg parseAndRefreshJWT(String jwt){
        ResponseMsg msg = new ResponseMsg();
        //捕获解析jwt过程中的相关异常
        try {
            Claims c = parseJWT(jwt);
            int UserId = (int) c.get("UserId");
            //解析成功生成新的jwt，期限30分钟，并返回新的jwt与成功信息
            String newJWT = createJWT(UserId);
            System.out.println("新jwt的id: "+c.getId());
            System.out.println("新jwt的签发时间: "+c.getIssuedAt());
            System.out.println("新jwt的到期时间: "+c.getExpiration());
            System.out.println("新jwt的用户的id: "+c.get("UserId"));
            System.out.println("新jwt： "+newJWT);
            msg.setStatus(200);
            msg.setMessage("jwt认证成功！");
            //捕获到jwt过期异常，返回相关信息
        }catch (ExpiredJwtException eje){
            msg.setStatus(500);
            msg.setMessage("ExpiredJwtException JWT已过期，请重新登录！");


            //如果伪造了jwt，解析jwt的时候会捕获到jwt不合法错误，并返回相关信息
        }catch (Exception e){
            msg.setStatus(500);
            msg.setMessage("Exception JWT不合法！");

        }
        return msg;
    }


    public static void main(String[] args) {
        String jwt =   createJWT(123);
        System.out.println(jwt);

        ResponseMsg msg =  parseAndRefreshJWT(jwt);
        System.out.println(msg);
    }
}
